package fr.ssd.ci.certificat;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import fr.ssd.ci.Constants;
import fr.ssd.ci.keystore.KeystoreLoader;

public class Installer {

	public void installCertificat(String host) throws KeyStoreException,
			NoSuchAlgorithmException, CertificateException, IOException,
			KeyManagementException {
		installCertificat(host, Constants.DEFAULT_PORT);
	}

	public void installCertificat(String host, int port)
			throws KeyStoreException, NoSuchAlgorithmException,
			CertificateException, IOException, KeyManagementException {
		installCertificat(host, port, Constants.DEFAULT_PASSPHRASE);
	}

	public void installCertificat(String host, int port, String pass)
			throws KeyStoreException, NoSuchAlgorithmException,
			CertificateException, IOException, KeyManagementException {

		final char[] passphrase = pass.toCharArray();

		KeyStore ks = KeystoreLoader.load(passphrase);

		SSLContext context = SSLContext.getInstance("TLS");
		TrustManagerFactory tmf = TrustManagerFactory
				.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		tmf.init(ks);
		X509TrustManager defaultTrustManager = (X509TrustManager) tmf
				.getTrustManagers()[0];
		SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
		context.init(null, new TrustManager[] { tm }, null);
		SSLSocketFactory factory = context.getSocketFactory();
		if (!isTrustedCertificate(host, port, factory)) {

			X509Certificate[] chain = tm.chain;
			if (chain == null) {
				System.out.println("Could not obtain server certificate chain");
				return;
			}

			System.out.println();
			System.out.println("Server sent " + chain.length
					+ " certificate(s):");
			System.out.println();
			MessageDigest sha1 = MessageDigest.getInstance("SHA1");
			MessageDigest md5 = MessageDigest.getInstance("MD5");

			OutputStream out = new FileOutputStream(
					Constants.DEFAULT_KEYSTORE_FILENAME);

			for (int i = 0; i < chain.length; i++) {
				X509Certificate cert = chain[i];
				System.out.println(" " + (i + 1) + " Subject "
						+ cert.getSubjectDN());
				System.out.println("   Issuer  " + cert.getIssuerDN());
				sha1.update(cert.getEncoded());
				System.out.println("   sha1    "
						+ HexUtil.toHexString(sha1.digest()));
				md5.update(cert.getEncoded());
				System.out.println("   md5     "
						+ HexUtil.toHexString(md5.digest()));
				System.out.println();

				String alias = host + "-" + (i + 1);
				ks.setCertificateEntry(alias, cert);
				ks.store(out, passphrase);
				System.out.println("Added certificate to keystore '"
						+ Constants.DEFAULT_KEYSTORE_FILENAME
						+ "' using alias '" + alias + "'");

			}

			out.close();
		}
	}

	public boolean isTrustedCertificate(String host, int port,
			SSLSocketFactory factory) throws IOException, UnknownHostException,
			SocketException, NoSuchAlgorithmException {

		boolean retval = true;

		System.out
				.println("Opening connection to " + host + ":" + port + "...");
		SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
		socket.setSoTimeout(10000);
		try {
			System.out.println("Starting SSL handshake...");
			socket.startHandshake();
			socket.close();
			System.out.println();
			System.out.println("No errors, certificate is already trusted");
		} catch (SSLException e) {
			System.out.println();
			e.printStackTrace(System.out);
			retval = false;
		}
		return retval;
	}

	/**
	 * @return
	 */
	public String getDefaultKeyStoreFileName() {
		return Constants.DEFAULT_KEYSTORE_PATH + Constants.SEPARATOR
				+ Constants.DEFAULT_KEYSTORE_FILENAME;
	}

}